Add KEDA ScaledObject support to WorkerResourceTemplate#285
Open
gibbonjj wants to merge 14 commits intotemporalio:mainfrom
Open
Add KEDA ScaledObject support to WorkerResourceTemplate#285gibbonjj wants to merge 14 commits intotemporalio:mainfrom
gibbonjj wants to merge 14 commits intotemporalio:mainfrom
Conversation
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Three fixes from final branch review: - RenderWorkerResourceTemplate processing-order godoc now lists token substitution as step 2 and renumbers downstream steps. - validateWorkerResourceTemplateSpec step comments close the 3 → 5 gap (the scale-to-zero switch is now labeled step 4). - scaledObjectForIntegration comment no longer implies the webhook does token substitution (substitution is at controller render time). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds examples/wrt-keda-temporal.yaml showing the `type: temporal` trigger (KEDA >= 2.17) as a complement to the existing Prometheus-trigger example. The native scaler queries Temporal's gRPC API directly — no metrics pipeline dependency, native per-buildId scoping via the `buildId` metadata field rather than a metric label. Updates docs/worker-resource-templates.md to: - Frame the two trigger types with a clear "prefer native when X, prefer Prometheus when Y" rubric. - Restructure the KEDA example into Prometheus and native-temporal subsections, each with a full WRT spec. - Clarify that token substitution covers any string field, making both trigger types work with the same substitution mechanism. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Collaborator
|
Hi @gibbonjj, thank you for building this! We plan to support KEDA ScaledObject as a WorkerResourceTemplate as soon as possible. The currently-released Once KEDA v2.20 is released with support for Worker Deployments, we will support it in the controller. You can track the issue here: #286 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds KEDA
ScaledObjectas a supported kind underWorkerResourceTemplate, with a new token-substitution mechanism and a kind-aware scale-to-zero guard. Additive; HPA paths are unchanged.Motivation
Kubernetes permits only one
external.metrics.k8s.ioAPIService per cluster. In clusters running KEDA, that slot is occupied bykeda-operator-metrics-apiserver, which preventsHorizontalPodAutoscalerwithtype: Externalmetrics from resolving against any other provider. Users in this (common) configuration can't use the existing HPA-basedWorkerResourceTemplatepattern for backlog-driven autoscaling — they need to produce KEDA-nativeScaledObjects that KEDA watches directly.The existing
spec.metrics[*].external.metric.selector.matchLabelslabel injection doesn't apply to KEDA Prometheus triggers — those carry a freeform PromQLquerystring rather than a structured label selector. This PR introduces token substitution as a complementary, general-purpose mechanism.What's in the PR
internal/k8s/tokens.go). Recursively substitutes three tokens in every string leaf of the rendered template:__TEMPORAL_WORKER_DEPLOYMENT_NAME__,__TEMPORAL_WORKER_BUILD_ID__,__TEMPORAL_NAMESPACE__. Values mirror the existingmatchLabelsinjection 1:1. Unknown__FOO__-style tokens pass through unchanged. Runs beforeautoInjectFieldsinRenderWorkerResourceTemplate, so structured injection downstream never sees unresolved tokens.api/v1alpha1/workerresourcetemplate_webhook.go). The existing unconditionalminReplicas: 0rejection is replaced with a kind switch: HPA guardsminReplicas, ScaledObject guards bothminReplicaCountandidleReplicaCount. SharedscaleToZeroRationaleconstant keeps both messages in sync. Same Temporal-side reason as before:approximate_backlog_countis not emitted when the task queue is idle with no pollers, so a metric-based autoscaler cannot detect new work after scaling to zero. The existing\"minReplicas must not be 0\"substring is preserved (two existing tests assert on it).scaleTargetRefinjection. KEDA'sScaledObject.spec.scaleTargetRefaccepts a superset of what the controller already injects (apiVersion: apps/v1,kind: Deployment,name). The existing recursive injection atinternal/k8s/workerresourcetemplates.goworks unchanged for KEDA; a render test proves it.api/v1alpha1/testdata/keda/scaledobject-crd.yaml(usesx-kubernetes-preserve-unknown-fields: true— just enough for RESTMapper resolution; no upstream version coupling).webhook_suite_test.gopoints at it and addsScaledObjecttoALLOWED_KINDS. A happy-path admission test inworkerresourcetemplate_webhook_integration_test.goexercises the full kube-apiserver → admission webhook → SAR path.examples/wrt-keda-prometheus.yamlparallels the existing HPA example.docs/worker-resource-templates.mdgains a "Token substitution" section, a KEDA example section, and an updated "Allowed resource kinds and RBAC" entry.helm/temporal-worker-controller/values.yamlgets a commented-out ScaledObject stanza — default chart behavior is unchanged (users who don't run KEDA are unaffected).Design choices worth flagging
triggers[*]wheretype: prometheusand rewritemetadata.queryto append per-version label filters. This would require parsing PromQL, which is brittle and couples the controller to KEDA's schema. Token substitution is schema-agnostic: it works for any current or future CRD whose metric configuration uses freeform strings.ScaledJobout of scope. ScaledJob is a different architectural pattern (per-task Jobs, noscaleTargetRefto a Deployment). Revisit if users ask.idleReplicaCount: 0is the primary scale-to-zero mechanism;minReplicaCount: 0is the secondary. Both are blocked for consistency with HPA policy and because the underlying Temporal-side metric behavior applies equally. If Temporal later emits a metric that survives queue idleness, the guard can relax.Usage
Users opting in add the ScaledObject entry to
workerResourceTemplate.allowedResources:Then author a single
WorkerResourceTemplatewith tokens in the PromQL query; the controller renders oneScaledObjectper active Build ID at reconcile time. Seeexamples/wrt-keda-prometheus.yamland the updateddocs/worker-resource-templates.mdfor full walkthroughs.Test plan
go test ./...— all packages pass, including the new unit tests (token engine, render-level substitution, ScaledObject render determinism) and envtest integration test (happy-path admission of a ScaledObject-backed WRT).go vet ./...— clean.\"minReplicas must not be 0\"error substring is preserved.rbac.yamltemplate with the new ScaledObject entry uncommented).Commit structure
13 commits in strict TDD alternation (failing test → implementation → next failing test → …). Happy to squash on merge if preferred.
🤖 Generated with Claude Code